package control

import (
	"fmt"
	"gitee.com/burybell/images-bed/fs"
	"gitee.com/burybell/images-bed/tool"
	"github.com/go-chi/chi"
	"github.com/go-chi/chi/middleware"
	"github.com/go-chi/httprate"
	"io/ioutil"
	"net/http"
	"path"
	"strconv"
	"strings"
	"time"
)

func Run(dataPath string, suffixes []string) *chi.Mux {

	if dataPath == "" {
		dataPath = "data/"
	}

	if suffixes == nil {
		suffixes = make([]string, 0)
		suffixes = append(suffixes, "png")
		suffixes = append(suffixes, "jpg")
		suffixes = append(suffixes, "jpeg")
		suffixes = append(suffixes, "gif")
		suffixes = append(suffixes, "bmp")
	}
	fs.OpenFs(dataPath, suffixes)
	go fmt.Println(fs.GetBanner())
	return Router("/v1")
}


func Router(prefix string) *chi.Mux {
	app := chi.NewRouter()
	app.Use(middleware.Logger)
	app.Use(middleware.RequestID)
	app.Use(middleware.RealIP)
	app.Use(middleware.Recoverer)
	app.Route(prefix, func(router chi.Router) {

		// 场景创建
		router.Route("/scene", func(inner chi.Router) {
			// 场景接口限速
			inner.Use(httprate.Limit(10,
				10*time.Second,
				httprate.WithKeyFuncs(httprate.KeyByIP, httprate.KeyByEndpoint)))
			// 获取所有场景
			inner.Get("/", func(writer http.ResponseWriter, request *http.Request) {
				tool.ServeJson(writer, http.StatusOK, fs.GetScenes())
			})

			// 创建场景
			inner.Post("/{name}", func(writer http.ResponseWriter, request *http.Request) {
				name := chi.URLParam(request, "name")
				if fs.HasScene(name) {
					tool.ServeJson(writer, http.StatusBadRequest, "exist")
					return
				} else {
					err := fs.CreateScene(name)
					if err != nil {
						tool.ServeJson(writer, http.StatusExpectationFailed, "fail")
					}
					tool.ServeJson(writer, http.StatusOK, "ok")
					return
				}
			})

			// 删除场景
			inner.Delete("/{name}", func(writer http.ResponseWriter, request *http.Request) {
				name := chi.URLParam(request, "name")
				if fs.HasScene(name) {
					// 协程删除场景
					go fs.DeleteScene(name)
					tool.ServeJson(writer, http.StatusOK, "ok")
				} else {
					tool.ServeJson(writer, http.StatusAccepted, "not exist")
				}
			})

		})

		router.Route("/push", func(inner chi.Router) {
			inner.Use(httprate.Limit(
				3,
				9*time.Second,
				httprate.WithKeyFuncs(httprate.KeyByIP, httprate.KeyByEndpoint),
			))

			// 创建图片
			inner.Post("/{scene}", func(writer http.ResponseWriter, request *http.Request) {

				// 可自定义post表单 文件域名称
				field := request.URL.Query().Get("field")
				if field == "" {
					field = "file"
				}

				scene := chi.URLParam(request, "scene")
				file, info, err := request.FormFile(field)
				if err != nil {
					tool.ServeJson(writer, http.StatusBadRequest, "file null")
				}
				all, err := ioutil.ReadAll(file)
				if err != nil {
					tool.ServeJson(writer, http.StatusBadRequest, "fail")
				}

				put, err := fs.Put(scene, path.Base(info.Filename), info.Size, all)
				if err != nil {
					if err == fs.ImageTypeNotSupport {
						tool.ServeJson(writer, http.StatusBadRequest, "image not support")
					} else if err == fs.SceneNotExist {
						tool.ServeJson(writer, http.StatusBadRequest, "scene not exist")
					} else {
						tool.ServeJson(writer, http.StatusBadRequest, "error")
					}
				} else {
					tool.ServeJson(writer, http.StatusOK, put)
				}
			})
		})

		// 预览，带图片压缩功能
		router.Route("/view", func(inner chi.Router) {
			inner.Use(httprate.Limit(
				10,
				10*time.Second,
				httprate.WithKeyFuncs(httprate.KeyByIP, httprate.KeyByEndpoint),
			))

			// 预览图片
			inner.Get("/{md5}", func(writer http.ResponseWriter, request *http.Request) {
				md5 := chi.URLParam(request, "md5")
				image, bytes, err := fs.GetImage(md5)
				if err != nil {
					writer.WriteHeader(http.StatusBadRequest)
					return
				}
				compress := request.URL.Query().Get("compress")
				if quality, err := strconv.Atoi(compress); err == nil {
					if quality <= 0 || quality > 100 {
						quality = 75
					}
					compressImage := tool.CompressImage(bytes, quality)
					write, err := writer.Write(compressImage)
					if err != nil || write <= 0 {
						writer.WriteHeader(http.StatusBadRequest)
					} else {
						writer.Header().Add("content-type", "image/"+image.Suffix)
						writer.WriteHeader(http.StatusOK)
					}
				} else {
					write, err := writer.Write(bytes)
					if err != nil || write <= 0 {
						writer.WriteHeader(http.StatusBadRequest)
					} else {
						writer.Header().Add("content-type", "image/"+image.Suffix)
						writer.WriteHeader(http.StatusOK)
					}
				}
			})
		})

		// 查询信息
		router.Route("/info", func(inner chi.Router) {
			inner.Use(httprate.Limit(
				40,
				10*time.Second,
				httprate.WithKeyFuncs(httprate.KeyByIP, httprate.KeyByEndpoint),
			))

			// 查询图片信息
			inner.Get("/{md5}", func(writer http.ResponseWriter, request *http.Request) {
				md5 := chi.URLParam(request, "md5")
				image, err := fs.Get(md5)
				if err != nil {
					writer.WriteHeader(http.StatusBadRequest)
					return
				}
				tool.ServeJson(writer, http.StatusOK, image)
			})
		})

		router.Route("/delete", func(inner chi.Router) {
			inner.Use(httprate.Limit(
				5,
				10*time.Second,
				httprate.WithKeyFuncs(httprate.KeyByIP, httprate.KeyByEndpoint),
			))

			// 删除图片
			inner.Delete("/{md5}", func(writer http.ResponseWriter, request *http.Request) {
				md5 := chi.URLParam(request, "md5")
				image, err := fs.Get(md5)
				if err != nil {
					writer.WriteHeader(http.StatusBadRequest)
					return
				}

				err = fs.DeleteImage(image)
				if err != nil {
					writer.WriteHeader(http.StatusBadRequest)
					return
				}

				tool.ServeJson(writer, http.StatusOK, image)
			})
		})

		// 文件下载，支持断点续传
		router.Route("/download", func(inner chi.Router) {
			inner.Use(httprate.Limit(
				10,
				10*time.Second,
				httprate.WithKeyFuncs(httprate.KeyByIP, httprate.KeyByEndpoint),
			))

			inner.Get("/{md5}", func(writer http.ResponseWriter, request *http.Request) {
				md5 := chi.URLParam(request, "md5")
				image, err := fs.Get(md5)
				if err != nil {
					writer.WriteHeader(http.StatusBadRequest)
					return
				}

				writer.Header().Add("Accept-Ranges", "bytes")
				writer.Header().Add("Content-Disposition", "attachment; filename="+image.FileName)

				var start, end int64
				if rge := request.Header.Get("Range"); rge != "" {
					fmt.Println("range" + rge)
					if strings.Contains(rge, "bytes=") && strings.Contains(rge, "-") {
						_, err = fmt.Sscanf(rge, "bytes=%d-%d", &start, &end)
						if err != nil {
							start = 0
							end = image.Size - 1
						} else {
							if end == 0 {
								end = image.Size - 1
							}
							if start > end || start < 0 || end < 0 || end >= image.Size {
								writer.WriteHeader(http.StatusRequestedRangeNotSatisfiable)
								return
							}
							writer.Header().Add("Content-Length", strconv.FormatInt(end-start+1, 10))
							writer.Header().Add("Content-Range", fmt.Sprintf("bytes %v-%v/%v", start, end, image.Size))
							writer.WriteHeader(http.StatusPartialContent)
						}
					} else {
						writer.WriteHeader(http.StatusBadRequest)
						return
					}
				} else {
					start = 0
					end = image.Size - 1
				}

				_, bytes, err := fs.GetPartImage(md5, start, end)
				if err != nil {
					writer.WriteHeader(http.StatusForbidden)
					return
				}

				fmt.Printf("start = %d, end = %d\n", start, end)
				write, err := writer.Write(bytes)
				if err != nil || write <= 0 {
					writer.WriteHeader(http.StatusBadRequest)
				} else {
					writer.Header().Add("content-type", "image/"+image.Suffix)
					writer.WriteHeader(http.StatusOK)
				}
			})
		})

		router.Route("/search", func(inner chi.Router) {
			inner.Use(httprate.Limit(
				5,
				10*time.Second,
				httprate.WithKeyFuncs(httprate.KeyByIP, httprate.KeyByEndpoint),
			))
			inner.Get("/{keyword}", func(writer http.ResponseWriter, request *http.Request) {
				keyword := chi.URLParam(request, "keyword")
				images := fs.SearchImages(keyword)
				tool.ServeJson(writer, http.StatusOK, images)
			})
		})
	})
	return app
}
